#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lambda/lambda.hpp>
#include <skyroad/util/convert_to.hpp>

#include "request_parser.hpp"
#include "request.hpp"

request_parser::request_parser(): state_(method_start)
{

}

void request_parser::reset()
{
	state_ = method_start;
}

boost::tribool request_parser::consume(request& req, char input)
{
//debug(boost::format("input=%1%") % input);
    switch (state_)
    {
    case content:
        if (req.content.size() < content_size_) {
            req.content.push_back(input);
            if (req.content.size() == content_size_) {
                reset();
                return true;
            } else {
                return boost::indeterminate;
            }
        } else
            return false;
    case method_start:
        if (!is_char(input) || is_ctl(input) || is_tspecial(input))
        {
            //debug("error method_start");
            return false;
        }
        else
        {
            state_ = method;
            req.method.push_back(input);
            return boost::indeterminate;
        }
    case method:
        if (input == ' ')
        {
            boost::to_upper(req.method);
            state_ = uri;
            return boost::indeterminate;
        }
        else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
        {
            //debug("error method");
            return false;
        }
        else
        {
            req.method.push_back(input);
            return boost::indeterminate;
        }
    case uri_start:
        if (is_ctl(input))
        {
            //debug("error uri_start");
            return false;
        }
        else
        {
            state_ = uri;
            req.uri.push_back(input);
            return boost::indeterminate;
        }
    case uri:
        if (input == ' ')
        {
            state_ = version_h;
            return boost::indeterminate;
        }
        else if (is_ctl(input))
        {
            //debug("error uri");
            return false;
        }
        else
        {
            req.uri.push_back(input);
            return boost::indeterminate;
        }
    case version_h:
        if (input == 'H')
        {
            state_ = version_t_1;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_h");
            return false;
        }
    case version_t_1:
        if (input == 'T')
        {
            state_ = version_t_2;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_t_1");
            return false;
        }
    case version_t_2:
        if (input == 'T')
        {
            state_ = version_p;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_t_2");
            return false;
        }
    case version_p:
        if (input == 'P')
        {
            state_ = version_slash;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_p");
            return false;
        }
    case version_slash:
        if (input == '/')
        {
            req.version_major = 0;
            req.version_minor = 0;
            state_ = version_major_start;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_slash");
            return false;
        }
    case version_major_start:
        if (is_digit(input))
        {
            req.version_major = req.version_major * 10 + input - '0';
            state_ = version_major;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_major_start");
            return false;
        }
    case version_major:
        if (input == '.')
        {
            state_ = version_minor_start;
            return boost::indeterminate;
        }
        else if (is_digit(input))
        {
            req.version_major = req.version_major * 10 + input - '0';
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_major");
            return false;
        }
    case version_minor_start:
        if (is_digit(input))
        {
            req.version_minor = req.version_minor * 10 + input - '0';
            state_ = version_minor;
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_minor_start");
            return false;
        }
    case version_minor:
        if (input == '\r')
        {
            state_ = expecting_newline_1;
            return boost::indeterminate;
        }
        else if (is_digit(input))
        {
            req.version_minor = req.version_minor * 10 + input - '0';
            return boost::indeterminate;
        }
        else
        {
            //debug("error version_minor");
            return false;
        }
    case expecting_newline_1:
        if (input == '\n')
        {
            state_ = header_line_start;
            return boost::indeterminate;
        }
        else
        {
            //debug("error expecting_newline_1");			
            return false;
        }
    case header_line_start:
        if (input == '\r')
        {
            state_ = expecting_newline_3;
            return boost::indeterminate;
        }
        else if (!req.headers.empty() && (input == ' ' || input == '\t'))
        {
            state_ = header_lws;
            return boost::indeterminate;
        }
        else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
        {
            //debug("error header_line_start");
            return false;
        }
        else
        {
            req.headers.push_back(h_key_value());
            req.headers.back().name.push_back(input);
            state_ = header_name;
            return boost::indeterminate;
        }
    case header_lws:
        if (input == '\r')
        {
            state_ = expecting_newline_2;
            return boost::indeterminate;
        }
        else if (input == ' ' || input == '\t')
        {
            return boost::indeterminate;
        }
        else if (is_ctl(input))
        {
            return false;
        }
        else
        {
            state_ = header_value;
            req.headers.back().value.push_back(input);
            return boost::indeterminate;
        }
    case header_name:
        if (input == ':')
        {
            boost::to_lower(req.headers.back().name);
            state_ = header_value;
            return boost::indeterminate;
        }
        else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
        {
            //debug("error header_name");
            return false;
        }
        else
        {
            req.headers.back().name.push_back(input);
            return boost::indeterminate;
        }
    case space_before_header_value:
        if (input == ' ')
        {
            state_ = header_value;
            return boost::indeterminate;
        }
        else
        {
            //debug("error space_before_header_value");
            return false;
        }
    case header_value:
        if (input == '\r')
        {
            state_ = expecting_newline_2;
            return boost::indeterminate;
        }
        else if (is_ctl(input))
        {
            return false;
        }
        else
        {
            req.headers.back().value.push_back(input);
            return boost::indeterminate;
        }
    case expecting_newline_2:
        if (input == '\n')
        {
            if (req.headers.size() > 0)
                boost::trim(req.headers.back().value);
            state_ = header_line_start;
            return boost::indeterminate;
        }
        else
        {
            //debug("error expecting_newline_2");
            return false;
        }
    case expecting_newline_3:
        if (input == '\n') {
            if ("GET" == req.method) 
            {
                return true;
            }
            else if ("POST" == req.method) 
            {

                    t_vt_hk_value::iterator it = 
                        std::find_if(
                            req.headers.begin(), 
                            req.headers.end(), 
                            boost::bind(&h_key_value::name, _1) == "content-length");

                    if (it == req.headers.end())
                        return false;

                    content_size_ = atoi(it->value.c_str());

                    if (content_size_ <=0 || content_size_ >= 1024*16)
                        return false;
                    req.content.clear();
                    req.content.reserve(content_size_);


                state_ = content;
                return boost::indeterminate;
            }
            else 
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    default:
        return false;
    }
}

bool request_parser::is_char(int c)
{
	return c >= 0 && c <= 127;
}

bool request_parser::is_ctl(int c)
{
	return c >= 0 && c <= 31 || c == 127;
}

bool request_parser::is_tspecial(int c)
{
	switch (c)
	{
	case '(': case ')': case '<': case '>': case '@':
	case ',': case ';': case ':': case '\\': case '"':
	case '/': case '[': case ']': case '?': case '=':
	case '{': case '}': case ' ': case '\t':
		return true;
	default:
		return false;
	}
}

bool request_parser::is_digit(int c)
{
	return c >= '0' && c <= '9';
}

 

